package com.xhj.ipersistent.builder;

import com.xhj.ipersistent.config.Configuration;
import com.xhj.ipersistent.mapping.MappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.List;

/**
 * XMLMappedStatementBuilder
 *
 * @author XJks
 * @description 解析对应实体类数据表的映射配置文件XXXMapper.xml，将解析出来的信息封装到MappedStatement中，并存储到Configuration中
 */
public class XMLMappedStatementBuilder {
    /**
     * 全局配置对象
     */
    private Configuration configuration;

    /**
     * TODO:
     *  构造方法，传入Configuration对象。
     *  在parse方法中，通过解析传入的XXXMapper.xml配置文件，填充Configuration对象。
     */
    public XMLMappedStatementBuilder(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * TODO:
     *  解析传入的XXXMapper.xml配置文件，填充Configuration对象。
     *  该方法主要使用dom4j解析XXXMapper.xml文件，并将解析结果填充到Configuration对象中。
     *
     * @param resourceAsStream XXXMapper.xml配置文件流
     */
    public void parse(InputStream resourceAsStream) throws DocumentException {
        // 使用dom4j中SAXReader()解析XML配置文件，获取Document对象
        Document document = new SAXReader().read(resourceAsStream);
        // 获取XML文件的根节点，即<mapper></mapper>标签
        Element rootElement = document.getRootElement();
        // 获取根节点的namespace属性值，标签示例如下<mapper namespace="com.xhj.ipersistent.mapper.UserMapper"></mapper>
        String namespace = rootElement.attributeValue("namespace");

        // 解析根节点下的所有<select></select>标签
        List<Element> selectElementList = rootElement.selectNodes("//select");
        // 遍历<select></select>标签进行解析
        for (Element element : selectElementList) {
            // 获取<select></select>标签的id属性值，标签示例如<select id="selectById"></select>
            String id = element.attributeValue("id");
            // 获取<select></select>标签的resultType属性值，标签示例如<select id="selectById" resultType="com.xhj.entity.User"></select>
            String resultType = element.attributeValue("resultType");
            // 获取<select></select>标签的parameterType属性值，标签示例如<select id="selectById" parameterType="com.xhj.entity.User"></select>
            String parameterType = element.attributeValue("parameterType");
            // 获取<select></select>标签中的SQL语句，标签示例如<select id="selectById">select * from user where id = #{id}</select>
            String sql = element.getTextTrim();
            // 将上述信息封装为mappedStatement对象
            MappedStatement mappedStatement = new MappedStatement();
            // statementId的格式为:namespace.id，示例如下com.xhj.ipersistent.mapper.UserMapper.selectById
            String statementId = namespace + "." + id;
            // 填充mappedStatement对象
            mappedStatement.setStatementId(statementId);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setSql(sql);
            mappedStatement.setSqlCommandType("select");
            mappedStatement.initBoundSql();
            // 将封装好的mappedStatement封装到configuration中的map集合中
            configuration.getMappedStatementMap().put(statementId, mappedStatement);
        }

        // 解析根节点下的所有<insert></insert>标签
        List<Element> insertElementList = rootElement.selectNodes("//insert");
        // 遍历<insert></insert>标签进行解析
        for (Element element : insertElementList) {
            // 获取<insert></insert>标签的id属性值，标签示例如<insert id="insert"></insert>
            String id = element.attributeValue("id");
            // 获取<insert></insert>标签的resultType属性值，标签示例如<insert id="insert" resultType="com.xhj.entity.User"></insert>
            String resultType = element.attributeValue("resultType");
            // 获取<insert></insert>标签的parameterType属性值，标签示例如<insert id="insert" parameterType="com.xhj.entity.User"></insert>
            String parameterType = element.attributeValue("parameterType");
            // 获取<insert></insert>标签中的SQL语句，标签示例如<insert id="insert">insert into user(id, username) values(#{id}, #{username})</insert>
            String sql = element.getTextTrim();
            // 将上述信息封装为mappedStatement对象
            MappedStatement mappedStatement = new MappedStatement();
            // statementId的格式为:namespace.id，示例如下com.xhj.ipersistent.mapper.UserMapper.insert
            String statementId = namespace + "." + id;
            // 填充mappedStatement对象
            mappedStatement.setStatementId(statementId);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setSql(sql);
            mappedStatement.setSqlCommandType("insert");
            mappedStatement.initBoundSql();
            // 将封装好的mappedStatement封装到configuration中的map集合中
            configuration.getMappedStatementMap().put(statementId, mappedStatement);
        }

        // 解析根节点下的所有<update></update>标签
        List<Element> updateElementList = rootElement.selectNodes("//update");
        // 遍历<update></update>标签进行解析
        for (Element element : updateElementList) {
            // 获取<update></update>标签的id属性值，标签示例如<update id="update"></update>
            String id = element.attributeValue("id");
            // 获取<update></update>标签的resultType属性值，标签示例如<update id="updateById" resultType="com.xhj.entity.User"></update>
            String resultType = element.attributeValue("resultType");
            // 获取<update></update>标签的parameterType属性值，标签示例如<update id="updateById" parameterType="com.xhj.entity.User"></update>
            String parameterType = element.attributeValue("parameterType");
            // 获取<update></update>标签中的SQL语句，标签示例如<update id="updateById">update user set username = #{username} where id = #{id}</update>
            String sql = element.getTextTrim();
            // 将上述信息封装为mappedStatement对象
            MappedStatement mappedStatement = new MappedStatement();
            // statementId的格式为:namespace.id，示例如下com.xhj.ipersistent.mapper.UserMapper.updateById
            String statementId = namespace + "." + id;
            // 填充mappedStatement对象
            mappedStatement.setStatementId(statementId);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setSql(sql);
            mappedStatement.setSqlCommandType("update");
            mappedStatement.initBoundSql();
            // 将封装好的mappedStatement封装到configuration中的map集合中
            configuration.getMappedStatementMap().put(statementId, mappedStatement);
        }

        // 解析根节点下的所有<delete></delete>标签
        List<Element> deleteElementList = rootElement.selectNodes("//delete");
        // 遍历<delete></delete>标签进行解析
        for (Element element : deleteElementList) {
            // 获取<delete></delete>标签的id属性值，标签示例如<delete id="deleteById"></delete>
            String id = element.attributeValue("id");
            // 获取<delete></delete>标签的resultType属性值，标签示例如<delete id="deleteById" resultType="com.xhj.entity.User"></delete>
            String resultType = element.attributeValue("resultType");
            // 获取<delete></delete>标签的parameterType属性值，标签示例如<delete id="deleteById" parameterType="com.xhj.entity.User"></delete>
            String parameterType = element.attributeValue("parameterType");
            // 获取<delete></delete>标签中的SQL语句，标签示例如<delete id="deleteById">delete from user where id = #{id}</delete>
            String sql = element.getTextTrim();
            // 将上述信息封装为mappedStatement对象
            MappedStatement mappedStatement = new MappedStatement();
            // statementId的格式为:namespace.id，示例如下com.xhj.ipersistent.mapper.UserMapper.deleteById
            String statementId = namespace + "." + id;
            // 填充mappedStatement对象
            mappedStatement.setStatementId(statementId);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setSql(sql);
            mappedStatement.setSqlCommandType("delete");
            mappedStatement.initBoundSql();
            // 将封装好的mappedStatement封装到configuration中的map集合中
            configuration.getMappedStatementMap().put(statementId, mappedStatement);
        }
    }
}
